#include "touch_drv.h"

/**
 * @desc  : I2C软实现初始化
 * @date  : 2024-11-21
 * @author: AnRui
 */
static void TouchGpioConfig(void) {
    rcu_periph_clock_enable(TOUCH_SDA_RCU);
    rcu_periph_clock_enable(TOUCH_SCL_RCU);
    rcu_periph_clock_enable(TOUCH_RST_RCU);
    rcu_periph_clock_enable(TOUCH_INT_RCU);
    gpio_init(TOUCH_SCL_PORT, GPIO_MODE_OUT_OD, GPIO_OSPEED_10MHZ, TOUCH_SCL_PIN);
    gpio_init(TOUCH_SDA_PORT, GPIO_MODE_OUT_OD, GPIO_OSPEED_10MHZ, TOUCH_SDA_PIN);
    gpio_init(TOUCH_RST_PORT, GPIO_MODE_OUT_PP, GPIO_OSPEED_10MHZ, TOUCH_RST_PIN);  // RST
    gpio_init(TOUCH_INT_PORT, GPIO_MODE_OUT_PP, GPIO_OSPEED_10MHZ, TOUCH_INT_PIN); // INT
}


static void I2cSoftSendStop(void);

/**
 * @desc  : 主设备向从设备发送开始信号
*  @brief    SCL为高电平时，SDA由高电平向低电平跳变，开始传输数据
*           生成下图所示的波形图，即为起始时序
*                1 2    3     4
*                 _____________
*           SCL :              \_____
*                 ________
*           SDA :         \___________
 * @date  : 2024-11-19
 * @author: AnRui
 */
static void I2cSoftStart(void) {
    TOUCH_SDA(1);
    TOUCH_SCL(1);
    TOUCH_I2C_DELAY();

    TOUCH_SDA(0);
    TOUCH_I2C_DELAY();

    TOUCH_SCL(0);
    TOUCH_I2C_DELAY();
}

/**
* @desc  : 发送数据
*                1 2     3      4
*                         ______
*           SCL: ________/      \______
*                ______________________
*           SDA: \\\___________________
 *
 * @date  : 2024-11-19
 * @author: AnRui
 * @param :  data - {参数说明}:
 */
static void I2cSoftSendData(uint8_t data) {
    uint8_t i;
    for (i = 0;i < 8;i++) {
        //发送方法主要集中在这 每次根据最高位来发 下面依次把低位移到高位上去
        if (data & 0x80) {
            TOUCH_SDA(1);
        } else {
            TOUCH_SDA(0);
        }
        TOUCH_I2C_DELAY();
        TOUCH_SCL(1);
        TOUCH_I2C_DELAY();
        TOUCH_SCL(0);
        TOUCH_I2C_DELAY();
        data <<= 1;
    }
}


/**
 * @desc  : 等待响应
 *
 *             ____
 *            |    |
 *  SCL ______|    |_______
 *         ___       ______
 *        |   *     |
 *  SDA __|   ******|
 *
 *  注意: *位置代码从设备拉低
 * @date  : 2024-11-19
 * @author: AnRui
 */
static TouchResult_e I2cSoftWaitAck(void) {
    TOUCH_SET_SDA_MODE_IN(); // SDA交出控制权，SDA变为浮空输入  会自动把SDA置为高电平
    TOUCH_I2C_DELAY();       // 等待从机拉低SDA 

    TOUCH_SCL(1);
    TOUCH_I2C_DELAY();
    uint8_t retry = TOUCH_RETRY_TIMES;
    while (TOUCH_GET_SDA_PIN_STATE() == SET && retry > 0) {
        retry--;
        TOUCH_I2C_DELAY();
    }
    TOUCH_SCL(0);
    TOUCH_SET_SDA_MODE_OUT();
    if (retry <= 0) { return TOUCH_I2C_ERR_SLAVE_NO_ACK; }
    TOUCH_I2C_DELAY();
    return TOUCH_I2C_RESULT_OK;
}

/**
 * @desc  : 接收数据
 *          ______
 *         |      |
 *  SCL ___|      |___
 *        ___________
 *       |           |
 *  SDL  |           |  BIT1
 *
 *       |           |
 *  SDL  |           |  BIT0
 *        ———————————
 *
 * @date  : 2024-11-19
 * @author: AnRui
 * @return: uint8_t
 */
static uint8_t I2cSoftResv(void) {
    TOUCH_SET_SDA_MODE_IN();
    TOUCH_I2C_DELAY();
    // SDA应该交给从设备，主设备应该是输入状态
    uint8_t i, data;
    data = 0;
    for (i = 0; i < 8; i++) {
        TOUCH_SCL(1);// 开始设置数据有效性
        TOUCH_I2C_DELAY();
        data <<= 1;
        data |= TOUCH_GET_SDA_PIN_STATE();
        // data |= TOUCH_GET_SDA_PIN_STATE() << (7 - i);
        TOUCH_SCL(0);// 给机会从设备准备数据
        TOUCH_I2C_DELAY();
    }
    TOUCH_SET_SDA_MODE_OUT();
    TOUCH_I2C_DELAY();
    return data;
}


/**
 * @desc  : 主机发送ACK响应
 *           ______
 *          |      |
 *  SCL  ___|      |___
 *
 *      |
 *  SDA |
 *       ———————————————
 *
 *
 * @date  : 2024-11-19
 * @author: AnRui
 */
static void I2cSoftSendAck(void) {
    // 拉低SDA
    TOUCH_SDA(0);
    TOUCH_I2C_DELAY();
    TOUCH_SCL(1);
    TOUCH_I2C_DELAY();
    TOUCH_SCL(0);
    TOUCH_I2C_DELAY();

}

/**
 * @desc  : 发送空响应
 *
 *           ______
 *          |      |
 *  SCL  ___|      |___
 *
 *       ———————————————
 *      |
 *  SDA |
 *
 * @date  : 2024-11-19
 * @author: AnRui
 */
static void I2cSoftSendNack(void) {
    // 拉高SDA
    TOUCH_SDA(1);
    TOUCH_I2C_DELAY();

    // 拉高SCL
    TOUCH_SCL(1);
    TOUCH_I2C_DELAY();
    // 拉低SCL
    TOUCH_SCL(0);
    TOUCH_I2C_DELAY();
}



/**
 * @desc  : 主设备向从设备发送结束信号
 *        _________
 *       |
 *  SCL _|
 *           ______
 *          |
 *  SDL ____|
 *
 * @date  : 2024-11-19
 * @author: AnRui
 */
static void I2cSoftSendStop(void) {
    TOUCH_SDA(0);
    TOUCH_I2C_DELAY();
    TOUCH_SCL(1);
    TOUCH_I2C_DELAY();
    TOUCH_SDA(1);
    TOUCH_I2C_DELAY();
}

/**
 * @desc  : 读数据
 * @date  : 2024-11-19
 * @author: AnRui
 * @param :  reg  -{寄存器地址}:
 * @param :  data -{保存数据的数组的指针}:
 * @param :  len  -{数组长度}:
 * @note:   写数据流程:
 *          1.发送开始信号
 *          2.发送写地址 接收ack
 *          3.发送寄存器地址高8位  接收ack
 *          4.发送寄存器地址低8位  接收ack
 *          5.接受数据 发送ack 。。。。接受数据 发送nack
 *          6.发送结束信号
 *          注意:寄存器地址为16位
 */
static TouchResult_e TouchWriteData(uint16_t reg, uint8_t* data, uint32_t len) {
    TouchResult_e result = TOUCH_I2C_RESULT_OK;
    // 开始
    I2cSoftStart();
    // 发送设备地址
    I2cSoftSendData(TOUCH_DEV_WRITE_ADDR);
    // 等待ack
    if (TOUCH_I2C_RESULT_OK != I2cSoftWaitAck()) {
        result = TOUCH_I2C_ERR_DEVICE_ADDR;
        goto i2c_err;
    }
    // 发送寄存器高8位地址
    I2cSoftSendData((uint8_t)(reg >> 8) & 0xFF);
    // 等待ack
    if (TOUCH_I2C_RESULT_OK != I2cSoftWaitAck()) {
        result = TOUCH_I2C_ERR_REGISTER_ADDR;
        goto i2c_err;
    }
    // 发送寄存器低8位地址
    I2cSoftSendData((uint8_t)reg & 0xFF);
    // 等待ack
    if (TOUCH_I2C_RESULT_OK != I2cSoftWaitAck()) {
        result = TOUCH_I2C_ERR_REGISTER_ADDR;
        goto i2c_err;
    }

    for (uint32_t i = 0;i < len; i++) {
        I2cSoftSendData(data[i]);
        if (TOUCH_I2C_RESULT_OK != I2cSoftWaitAck()) {
            result = TOUCH_I2C_ERR_WRITE;
            goto i2c_err;
        }
    }
    I2cSoftSendStop();
    return result;
i2c_err:
    I2cSoftSendStop();
    return result;
}


/**
 * @desc  : 读数据
 * @date  : 2024-11-19
 * @author: AnRui
 * @param :  reg  -{寄存器地址}:
 * @param :  data -{保存数据的数组的指针}:
 * @param :  len  -{数组长度}:
 * @note:   读数据流程:
 *          1.发送开始信号
 *          2.发送写地址 接收ack
 *          3.发送寄存器地址高8位  接收ack
 *          4.发送寄存器地址低8位  接收ack
 *          5.发送读地址 接收ack
 *          6.接受数据 发送ack 。。。。接受数据 发送nack
 *          7.发送结束信号
 *          注意:寄存器地址为16位
 */
static TouchResult_e TouchReadData(uint16_t reg, uint8_t* data, uint32_t len) {
    TouchResult_e result = TOUCH_I2C_RESULT_OK;
    I2cSoftStart();
    // 发送设备写地址
    I2cSoftSendData(TOUCH_DEV_WRITE_ADDR);
    // 等待ack
    if (TOUCH_I2C_RESULT_OK != I2cSoftWaitAck()) {
        result = TOUCH_I2C_ERR_DEVICE_ADDR;
        goto i2c_err;
    }

    // 发送寄存器高8位地址
    I2cSoftSendData((uint8_t)(reg >> 8));
    // 等待ack
    if (TOUCH_I2C_RESULT_OK != I2cSoftWaitAck()) {
        result = TOUCH_I2C_ERR_REGISTER_ADDR;
        goto i2c_err;
    }


    // 发送寄存器低8位地址
    I2cSoftSendData((uint8_t)reg);
    // 等待ack
    if (TOUCH_I2C_RESULT_OK != I2cSoftWaitAck()) {
        result = TOUCH_I2C_ERR_REGISTER_ADDR;
        goto i2c_err;
    }
    I2cSoftStart();
    // 发送设备地址(读)
    I2cSoftSendData(TOUCH_DEV_WRITE_ADDR | 1);
    // 等待ack
    if (TOUCH_I2C_RESULT_OK != I2cSoftWaitAck()) {
        result = TOUCH_I2C_ERR_DEVICE_ADDR;
        goto i2c_err;
    }

    uint32_t i;
    for (i = 0; i < len; i++) {
        // 接收数据
        data[i] = I2cSoftResv();
        if (i == len - 1) {
            // 如果是最后一条，发送NACK
            I2cSoftSendNack();
        } else {
            // 如果不是最后一条，发送ACK
            I2cSoftSendAck();
        }
    }
    I2cSoftSendStop();
    return result;
i2c_err:
    I2cSoftSendStop();
    return result;
}

/**
 * @desc  : 设置从机(触摸芯片)的地址
 * @date  : 2025-06-05
 * @author: AnRui
 * @return: staic
 * @note  : 配置从机地址为0x28/0x29
 *          配置流程:
 *              1. Reset INT 拉低
 *              2. INT 拉高持续>=100us
 *              3. Reset拉高持续>=5ms
 *              4. INT 转成浮空输入
 */
static void TouchSetSlaveAdress() {
    TOUCH_INT(0);
    TOUCH_RST(0);
    //延时1ms
    DelayNms(1);
    TOUCH_INT(1);
    DelayNus(200);
    TOUCH_RST(1);
    DelayNms(10);
    gpio_init(TOUCH_INT_PORT, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_10MHZ, TOUCH_INT_PIN);
    DelayNms(10);
}


/**
 * @desc  : 触摸屏驱动初始化
 * @date  : 2025-06-05
 * @author: AnRui
 * @note  : 1.设置GPIO 2.设置从机地址为0x28 3.读取从机id
 */
void TouchDrvInit() {
    //引脚配置
    TouchGpioConfig();
    //设置从机地址
    TouchSetSlaveAdress();
    //读取从机ID 确认是否初始化成功
    uint8_t touchData[5] = { 0 };
    //读取touch_id
    TouchResult_e result = TouchReadData(GT911_PID_REG, touchData, 4);
    if (result == TOUCH_I2C_RESULT_OK) {
        touchData[4] = '\0';
        printf("touch init successful ,Touch ID: %s\r\n", touchData);
    } else {
        printf("touch init error,code %d !\r\n", result);
    }
}



/**
 * @desc  : 触点扫描
 * @date  : 2025-06-06
 * @author: AnRui
 * @note  : 两次扫描时间至少大于10ms
 *          读取 GT911_STATUS_REG 状态寄存器
 *              bit7  : butter status
 *              bit0-3:触点数量
 *          状态寄存器读取完 必须清除
 *          测试说明:
 *              1.出现有触点但是触点个数为0的情况 原因是这个时候我们的手指正抬起时
 *
 */
void TouchScan(TouchInfo_t* touchInfo) {
    static TouchInfo_t lastTouchInfo = { .state = TOUCH_UP };
    static uint64_t lastScanTime = 0; // 记录上一次扫描时间
    TouchResult_e result;             // 与设备的I2C通讯结果
    uint8_t touchStatus = 0;          // 触摸状态
    uint8_t touchPointNum = 0;        // 触摸点数量
    uint8_t touchPostionData[6] = { 0 };

    // 设备要求两次获取触点坐标的时间间隔为7~10ms
    // 如果小于间隔时间，获取的数据会不准确
    if ((GetSysRunTime() - lastScanTime) < DETECT_INTERVAL_TIME) {
        *touchInfo = lastTouchInfo;
        return;
    } else {
        lastScanTime = GetSysRunTime();
    }

    // 查询状态寄存器
    result = TouchReadData(GT911_STATUS_REG, &touchStatus, 1);
    if (result != TOUCH_I2C_RESULT_OK) {
        printf("TouchReadData error, code %d\n", result);
        touchInfo->state = TOUCH_UP;
        lastTouchInfo = *touchInfo;
        return;
    }

    // 检测触摸状态
    if ((touchStatus & 0x80) == 0) {
        // 没有触摸事件
        touchInfo->state = TOUCH_UP;
        lastTouchInfo = *touchInfo;
        return;
    } else {
        // 获取触摸点数量
        touchPointNum = touchStatus & 0x0F;
    }

    // 清除状态寄存器（标记数据已处理）
    touchStatus = 0;
    result = TouchWriteData(GT911_STATUS_REG, &touchStatus, 1);
    if (result != TOUCH_I2C_RESULT_OK) {
        printf("TouchWriteData error, code %d\n", result);
        touchInfo->state = TOUCH_UP;
        lastTouchInfo = *touchInfo;
        return;
    }

    // 检测触摸点数量是否合法
    if (touchPointNum == 0 || touchPointNum > TOUCH_POINT_MAX) {
        touchInfo->state = TOUCH_UP;
        lastTouchInfo = *touchInfo;
        return;
    }

    // 读取第一个触摸点的x、y坐标和面积数据
    result = TouchReadData(GT911_TP1_REG, touchPostionData, 6);
    if (result != TOUCH_I2C_RESULT_OK) {
        printf("TouchReadData error, code %d\n", result);
        touchInfo->state = TOUCH_UP;
        lastTouchInfo = *touchInfo;
        return;
    }

    // 解析触摸点数据
    touchInfo->state = TOUCH_DOWN;
    touchInfo->point.x = ((uint16_t)touchPostionData[1] << 8) | touchPostionData[0];
    touchInfo->point.y = ((uint16_t)touchPostionData[3] << 8) | touchPostionData[2];
    touchInfo->point.size = ((uint16_t)touchPostionData[5] << 8) | touchPostionData[4];
    lastTouchInfo = *touchInfo;
}




